S02-10 面向对象-UML
[TOC]
概述
什么是 UML
UML(Unified Modeling Language,统一建模语言) 并不是一种编程语言,而是一种用于软件系统设计和文档化的可视化建模工具。在 Java 这类强面向对象语言的学习和开发中,UML(尤其是类图)是理解面向对象思想、梳理业务逻辑以及学习设计模式的“通用语言”。
进一步,我们可以总结出几个关键字:软件建模语言、工业标准、面向对象、系统建模、架构设计、最佳时间。
在UML系统开发中有三个主要的模型:
- 「功能模型」 :从用户的角度展示系统的功能,包括用例图。
- 「对象模型」 :采用对象,属性,操作,关联等概念展示系统的结构和基础,包括类图、对象图。
- 「动态模型」 :展现系统的内部行为。包括序列图,活动图,状态图。
UML包含了一系列的图,最常用的有类图、时序图、用例图等。
类图
类图(Class Diagram) 是 UML 中最核心、使用最广泛的图表。它用于描述系统的静态结构,完美地映射了 Java 中的类、接口、属性、方法以及它们之间的相互关系。
掌握类图,不仅能帮助你快速看懂复杂的开源项目架构,更是学习和理解设计模式的基础。
类的 UML 描述
单个类的 UML 表示方法:
在类图中,一个 Java 类(或接口)通常被画成一个分为三层的矩形。
顶部:类名 (Class Name):
- 普通类:正体字显示类名(如
User)。 - 抽象类:类名用斜体字表示。
- 接口:在类名上方带有
<<interface>>或<<Interface>>构造型标签(如<<interface>> Runnable)。
- 普通类:正体字显示类名(如
中部:属性 (Attributes):
对应 Java 中的成员变量。书写格式为:
可见性 名称 : 类型 [ = 默认值 ]可见性符号(对应 Java 访问修饰符):
+代表public-代表private#代表protected~代表default(包可见/无修饰符) 示例:- age : int = 18表示 Java 中的private int age = 18;
底部:方法 (Operations/Methods):
对应 Java 中的方法。书写格式为:
可见性 名称(参数列表) : 返回类型示例:
+ getName() : String表示 Java 中的public String getName()。如果是静态方法,在 UML 中通常会带下划线。如果是抽象方法,方法名用斜体。
接口的 UML 描述
接口 的类图表述与类大致相同,不同的是接口名要添加 Interface 标识,且行为的可见性必须用 "+" 表示。如下图:

类之间的关系
类与类之间的 6 种关系(核心重点):
理解 UML 类图的关键在于看懂类与类之间的连线。在 Java 中,类之间的关系按照耦合度由弱到强,可以分为以下 6 种:
依赖关系
依赖关系 (Dependency) —— “用到”:
含义:一种临时性的、弱的引用关系。类 A 的修改会影响到类 B。
UML 图形:带箭头的虚线,箭头指向被依赖的类。
Java 体现:被依赖的类作为方法参数、方法返回值或在方法内部作为局部变量被使用。
javapublic class Driver { // Car 作为方法参数,Driver 依赖了 Car public void drive(Car car) { car.move(); } }
关联关系
关联关系 (Association) —— “知道”:
含义:一种结构化的、平等的、长期的关系。表示一个类“知道”另一个类的属性和方法。可以分为单向关联、双向关联和自关联。
UML 图形:带普通箭头的实线(单向)或不带箭头的实线(双向)。
Java 体现:被关联的类作为另一个类的成员变量(全局变量)。
javapublic class Employee { // IDCard 作为成员变量,Employee 关联了 IDCard private IDCard idCard; }
聚合关系
聚合关系 (Aggregation) —— “拥有 (Has-a) / 弱整体与部分”:
含义:关联关系的特例,表示“整体与部分”的关系。但整体和部分是可以分离的,拥有各自独立的生命周期。
UML 图形:带空心菱形的实线,空心菱形指向“整体”。
Java 体现:同样是成员变量,但通常通过构造器参数或 Setter 方法从外部传入(依赖注入)。
javapublic class Computer { private Mouse mouse; // 鼠标从外部传入,电脑销毁了,鼠标依然可以独立存在并插到别的电脑上 public void setMouse(Mouse mouse) { this.mouse = mouse; } }
组合关系
组合关系 (Composition) —— “包含 (Contains-a) / 强整体与部分”:
含义:也是“整体与部分”的关系,但是一种强依赖。部分不能脱离整体单独存在,整体销毁,部分也跟着销毁(同生共死)。
UML 图形:带实心菱形的实线,实心菱形指向“整体”。
Java 体现:也是成员变量,但通常在“整体”的内部(如构造器中)直接实例化“部分”。
javapublic class Bird { private Wing wing; public Bird() { // 翅膀在鸟内部创建,鸟死了,翅膀也没了 this.wing = new Wing(); } }
继承关系
泛化关系 (Generalization) —— “是一个 (Is-a) / 继承”:
含义:面向对象三大特性之一的继承关系。子类继承父类的所有属性和方法。
UML 图形:带空心三角箭头的实线,箭头指向父类。
Java 体现:使用
extends关键字。javapublic class Dog extends Animal { ... }
实现关系
实现关系 (Realization) —— “遵循契约”:
含义:类实现了某个接口定义的所有抽象方法。
UML 图形:带空心三角箭头的虚线,箭头指向接口。
Java 体现:使用
implements关键字。javapublic class UserServiceImpl implements UserService { ... }
学习建议与实践
学习建议与实践:
在实际的 Java 开发中,我们很少会从零开始用画图工具去手绘一个极其庞大的系统类图。类图更多是作为一种交流和梳理思路的工具。
当你在 IntelliJ IDEA 中编写或阅读复杂的 Java 代码时,如果遇到难以理清的类继承树或引用关系,可以直接在项目目录结构或代码编辑器中右键点击类名,选择 Diagrams -> Show Diagram(快捷键通常是 Ctrl + Alt + Shift + U)。IDEA 会自动根据现有的 Java 代码逆向生成 UML 类图,这对于分析源码(如 Spring 框架或 JDK 集合类源码)是非常直观和高效的利器。
时序图
如果说类图(Class Diagram)是展示系统静态结构的“骨架”,那么时序图(Sequence Diagram,也叫顺序图) 就是展示系统动态运行过程的“血液”。
时序图主要用于描述对象之间传递消息的时间顺序。在实际的 Java 开发中,它被广泛用于梳理复杂的业务流程(例如:用户登录鉴权流程、支付接口调用链路、微服务间的 RPC 调用等)。
时序图的核心构成要素
时序图的核心构成要素:
时序图主要是由对象、生命线、激活框和消息组成的。
对象与生命线
对象与生命线 (Object & Lifeline):
- 概念:代表参与交互的具体实体。生命线是一条垂直的虚线,表示对象在一段时间内的存在。
- 表示法:顶部是一个矩形框,里面写明对象名或类名(格式通常为
对象名: 类名,如果匿名可以写:类名)。往下延伸的虚线就是生命线。 - Java 体现:对应 Java 堆内存中被
new出来的实例对象(如UserServiceImpl的实例、UserController的实例)。
激活框 / 控制焦点
激活框 / 控制焦点 (Activation Box):
- 概念:表示对象正在执行某个操作(即占用 CPU 时间)。
- 表示法:生命线上的一个细长矩形。
- Java 体现:对应 Java 中某个对象的方法正在被执行(该方法被压入线程的虚拟机栈中)。激活框的顶部是方法开始执行,底部是方法执行完毕并出栈。
消息
消息 (Messages) —— 对象间的沟通桥梁:
消息是时序图中最重要的部分,代表对象之间的通信。不同的箭头代表不同的调用方式:
同步消息 (Synchronous Message)
表示法:实线 + 实心箭头。
Java 体现:这是最常见的普通方法调用。调用者调用某个方法后,必须等待被调用者执行完毕并返回结果,才能继续执行后面的代码(阻塞式)。
异步消息 (Asynchronous Message)
表示法:实线 + 空心(线型)箭头。
Java 体现:调用者发送消息后,不等待响应即可继续执行后面的操作。例如:开启一个新线程 (
new Thread().start())、向消息队列(RabbitMQ/Kafka)发送消息、或者调用了带有@Async注解的方法。返回消息 (Return Message)
表示法:虚线 + 空心(线型)箭头。
Java 体现:对应 Java 中的
return语句。表示被调用方法执行完毕,将控制权和返回值交还给调用者。(注:为了图表简洁,如果调用流向很清晰,返回消息有时会被省略)。自调用消息 (Self Message)
表示法:箭头从生命线出发,绕一圈又指回同一个激活框。
Java 体现:对应 Java 中类内部的私有方法调用或递归调用(即
this.methodName())。
逻辑控制:组合片段
逻辑控制:组合片段 (Combined Fragments):
Java 代码中充满了 if-else 和 for/while 循环,时序图使用组合片段来表达这些控制流。
常见的交互操作符包括:
alt(Alternative - 替代片段):- 作用:表示互斥的条件分支。
- Java 体现:对应
if-else或switch-case语句。框内会被虚线分成上下两部分,标注不同的条件(如[balance > 0]和[balance <= 0])。
opt(Option - 选项片段):- 作用:表示满足条件才会执行的分支(没有 else 部分)。
- Java 体现:对应单个
if语句。
loop(Loop - 循环片段):- 作用:表示重复执行的一段交互。
- Java 体现:对应
for、while、do-while循环。通常会在左上角标注循环条件,如[for each item in cart]。
经典 Java 业务场景映射示例
经典 Java 业务场景映射示例:
假设我们要描述一个简单的“电商下单支付”流程,用 Java 代码和时序图的思维来对应:
Java 代码逻辑:
public class OrderController {
private OrderService orderService;
public Result placeOrder(OrderReq req) {
// 1. 同步调用 Service
boolean success = orderService.createOrder(req);
if (success) {
return Result.ok("下单成功");
} else {
return Result.fail("库存不足");
}
}
}
public class OrderService {
private InventoryService inventoryService;
private MQProducer mqProducer;
public boolean createOrder(OrderReq req) {
// 2. 自调用,校验参数
this.validateParams(req);
// 3. 同步调用,扣减库存
boolean hasStock = inventoryService.deduct(req.getProductId());
// 4. alt 组合片段 (if-else 逻辑)
if (hasStock) {
// 5. 异步消息,发送延迟队列检查支付状态
mqProducer.sendAsyncMsg("order_created_topic", req.getOrderId());
return true; // 6. 返回消息
}
return false; // 6. 返回消息
}
private void validateParams(OrderReq req) { /* ... */ }
}在时序图中,这个过程会这样展现:
客户端 向
OrderController生命周期发起一个同步消息(placeOrder)。OrderController向OrderService发起同步消息(createOrder)。OrderService自身的生命线上出现一个自调用消息(validateParams)。OrderService向InventoryService发起同步消息(deduct)。接下来是一个
alt框:[hasStock == true]分支:OrderService向MQProducer发送一条异步消息(带空心箭头的实线)。然后通过返回消息(虚线)将true沿原路返回。[hasStock == false]分支:直接通过返回消息返回false。
实际开发建议
实际开发建议:
在现代 Java 开发中,手动拖拽画时序图非常耗时。开发者通常喜欢使用代码即图表 (Docs as Code) 的方式。例如使用 PlantUML 或 Mermaid 语法,通过写简单的脚本语言就能自动生成时序图,不仅方便维护,还能直接嵌入到 Markdown 文档中。同时,IntelliJ IDEA 也有诸如 SequenceDiagram 等插件,可以直接根据 Java 方法的调用链路自动生成时序图。
你需要我为你演示一段如何使用 PlantUML 语法来生成上述“下单支付”流程的时序图代码吗?
分隔-----------------------------------
类描述
「类」 在UML中通常以实线矩形框表示。矩形框中有若干分割线。分别表示类名、属性和方法。如下图所示:
类名:图中最上面的矩形框中为类名。如果字体为斜体 ,表示为抽象类 。(图中的上面部分)属性:类名下边的区域。(图中的中间部分)方法:(图中的下面部分)
说明:属性和方法前面的“+”、“-”和“#”表示访问级别:
+:public-:private#:protected
类之间的关系
类之间有六种关系:
- 继承
- 实现
- 关联
- 依赖
- 组合
- 聚合
继承
继承(Inherit) 是面向对象语言的三大特性(封装,继承,多态)之一。子类继承父类。
UML类图中继承关系使用空心三角形+实线表示。
实现
实现(Implement) 与继承类似,实现类继承接口中的方法。
UML类图中实现关系使用空心三角形+虚线表示。
关联
依赖关系通常表现为类的私有属性。
体验AI代码助手 代码解读复制代码// 企鹅类
public class Penguin {
// 天气类
private Climate climate;
}其UML类图表示如下:
UML类图中关联使用实线箭头表示。
依赖
「依赖」 关系体现为局部变量、方法的形参,或者对静态方法的调用。
体验AI代码助手 代码解读复制代码public class Programmer {
public void work(Computer computer){
}
}UML类图中依赖关系使用虚线箭头表示。
以下代码展示了依赖关系的三种具体代码实现:局部变量、方法的形参和对静态方法的调用。
体验AI代码助手 代码解读复制代码public class Person{
public void doSomething1(){
Car car = new Car();//局部变量
...
}
public void doSomething2(Car car){//方法参数
...
}
public void doSomething3(){
int price = Car.do();//静态方法调用
}
}组合
「组合」 是关联关系的一种,表示一种强的“拥有”关系。体现了严格的部分和整体的关系。部分和整体的生命周期一样。
体验AI代码助手 代码解读复制代码public class Bird {
private Wing wing;
public Bird() {
this.wing = new Wing();
}
}UML类图中组合关系使用实心菱形+实线表示。
聚合
「聚合」 是关联关系的一种,表示一种弱的“拥有”关系。
用Java代码表示大雁是群居动物,每只大雁都属于一个雁群,一个雁群可以有多只大雁。
天气凉了,树叶黄了。 。。。 一群大雁往南飞,一会排成“S”字,一会排成“B”字。 ——《秋天》出自人教版小学语文一年级课文
体验AI代码助手 代码解读复制代码public class WildGooseAggregate {
private List<WildGoose> wideGooses;
}UML类图中聚合关系使用空心菱形实线表示。
综合示例
前面介绍了类之间的6种关系。为了更好地理解这6种关系。下面使用一个完整的例子(汽车)。该示例中包含了这6种关系。
说明:
- 车的类图结构为,表示车是一个抽象类;
- 它有两个继承类:小汽车和自行车;它们之间的关系为**「实现」** 关系,使用带空心箭头的虚线表示;
- 小汽车为与SUV之间也是**「继承」** 关系,它们之间的关系为泛化关系,使用带空心箭头的实线表示;
- 小汽车与发动机之间是**「组合」** 关系,使用带实心箭头的实线表示;
- 学生与班级之间是**「聚合」** 关系,使用带空心箭头的实线表示;
- 学生与身份证之间为**「关联」** 关系,使用一根实线表示;
- 学生上学需要用到自行车,与自行车是一种**「依赖」** 关系,使用带箭头的虚线表示;